package cn.demoncat.util.lang;


import cn.demoncat.util.exception.ParamRuntimeException;
import cn.demoncat.util.lang.constant.DateConstant;
import cn.demoncat.util.lang.constant.StringConstant;
import cn.demoncat.util.lang.entity.MapSs;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

/**
 * 描述：JSON转换工具
 *
 * @author 延晓磊
 *
 * @since 2017年7月10日
 */
public final class JsonUtil {
	
	/* --------------- 字段序列化设置 ------------------
	 
	 // Jackson 忽略序列化字段，注解字段或get()/is()方法
	 @JsonIgnore
	 
	 // FastJSON 忽略序列化字段，注解字段或get()/is()方法；同时可以指定序列化格式
	 @JSONField(serialize=false,deserialize=false)
	  
	 1、除了字段的get/set外，不建议创建get/set实例方法，否则在序列化时会被读取；必须创建时使用@JSONField忽略

	 2、enum转JSON时，只会输出enum.name()，需要implements JSONSerializable，重写write（转换为map并输出，如 serializer.write(LinkedMapSo.getInstance("id",id, "name", name));）
	  
	 */
	private JsonUtil(){}

	static {
		// 更改全局序列化
		SerializeConfig serializeConfig = SerializeConfig.globalInstance;
		// - 日期处理
		serializeConfig.put(LocalDateTime.class, DateSerializer.instance);
		serializeConfig.put(LocalDate.class, DateSerializer.instance);
		serializeConfig.put(LocalTime.class, DateSerializer.instance);
		serializeConfig.put(Date.class, DateSerializer.instance);
		// - 大数处理（前端最多处理16位数值）
		serializeConfig.put(Long.class, LongSerializer.instance);
	}
	
	/**
	 * 通用序列化格式
	 */
	public static final SerializerFeature[] FEATURES = new SerializerFeature[] {
			// 不使用引用符，便于JS读取
			SerializerFeature.DisableCircularReferenceDetect,
			// 空值处理
			SerializerFeature.WriteNullStringAsEmpty,
			// 日期格式化：yyyy-MM-dd HH:mm:ss
			SerializerFeature.WriteDateUseDateFormat
		};
	
	/**
	 * 通用序列化格式-带美化
	 */
	public static final SerializerFeature[] PRETTY_FEATURES = new SerializerFeature[] {
			// 不使用引用符，便于JS读取
			SerializerFeature.DisableCircularReferenceDetect,
			// 空值处理
			SerializerFeature.WriteNullStringAsEmpty,
			// 日期格式化：yyyy-MM-dd HH:mm:ss
			SerializerFeature.WriteDateUseDateFormat,
			// 美化
			SerializerFeature.PrettyFormat
		};
		
	/**
	 * 通用序列化格式-不保留NULL属性
	 */
	public static final SerializerFeature[] FEATURES_SHORT = new SerializerFeature[] {
			// 不使用引用符，便于JS读取
			SerializerFeature.DisableCircularReferenceDetect,
			// 日期格式化：yyyy-MM-dd HH:mm:ss
			SerializerFeature.WriteDateUseDateFormat
		};
	
	/**
	 * 通用序列化格式-带美化-不保留NULL属性
	 */
	public static final SerializerFeature[] PRETTY_FEATURES_SHORT = new SerializerFeature[] {
			// 不使用引用符，便于JS读取
			SerializerFeature.DisableCircularReferenceDetect,
			// 日期格式化：yyyy-MM-dd HH:mm:ss
			SerializerFeature.WriteDateUseDateFormat,
			// 美化
			SerializerFeature.PrettyFormat
		};
	
	/**
	 * 功能描述：转换对象为JSON串
	 *
	 * @param obj
	 * @return
	 * 
	 * @author yxl
	 *
	 * @since 2017年3月10日
	 */
	public static String toString(Object obj){
		// 空
		if (obj == null) {
			return StringConstant.EMPTY;
		}
		// 字符串：FastJSON会对特殊字符串进行转义，所以此处直接返回
		if (obj instanceof String) {
			return (String)obj;
		}
		// 其它类型
		return JSON.toJSONString(obj,FEATURES);
	}
 
	/**
	 * 功能描述：转换对象为JSON串，忽略NULL属性
	 *
	 * @param obj
	 * @return
	 * 
	 * @author yxl
	 *
	 * @since 2017年3月10日
	 */
	public static String toStr(Object obj){
		// 空
		if (obj == null) {
			return StringConstant.EMPTY;
		}
		// 字符串：FastJSON会对特殊字符串进行转义，所以此处直接返回
		if (obj instanceof String) {
			return (String)obj;
		}
		// 其它类型
		return JSON.toJSONString(obj,FEATURES_SHORT);
	}
	
	/**
	 * 功能描述：转换对象为JSON串，格式化日期类型参数(默认为时间戳)
	 *
	 * @param obj
	 * @param dateFormat 日期格式化"yyyy-MM-dd HH:mm:ss.SSS"，{ @link DateFormatEnum }
	 * @return 
	 * 
	 * @author yxl
	 *
	 * @since 2017年3月10日
	 */
	public static String toString(Object obj, String dateFormat){
		// 空
		if (obj == null) {
			return StringConstant.EMPTY;
		}
		// 字符串：FastJSON会对特殊字符串进行转义，所以此处直接返回
		if (obj instanceof String) {
			return (String)obj;
		}
		// 其它类型
		return JSON.toJSONStringWithDateFormat(obj, dateFormat, FEATURES);
	}
	
	/**
	 * 功能描述：转换对象为JSON串，格式化日期类型参数(默认为时间戳)，忽略NULL属性
	 *
	 * @param obj
	 * @param dateFormat 日期格式化"yyyy-MM-dd HH:mm:ss.SSS"，{ @link DateFormatEnum }
	 * @return 
	 * 
	 * @author yxl
	 *
	 * @since 2017年3月10日
	 */
	public static String toStr(Object obj, String dateFormat){
		// 空
		if (obj == null) {
			return StringConstant.EMPTY;
		}
		// 字符串：FastJSON会对特殊字符串进行转义，所以此处直接返回
		if (obj instanceof String) {
			return (String)obj;
		}
		// 其它类型
		return JSON.toJSONStringWithDateFormat(obj, dateFormat, FEATURES_SHORT);
	}
	
	/**
	 * 功能描述：转换对象为JSON串并美化
	 *
	 * @param obj
	 * @return
	 * 
	 * @author yxl
	 *
	 * @since 2017年3月10日
	 */
	public static String toFormatString(Object obj){
		// 空
		if (obj == null) {
			return StringConstant.EMPTY;
		}
		// 字符串：FastJSON会对特殊字符串进行转义，所以此处直接返回
		if (obj instanceof String) {
			return (String)obj;
		}
		// 其它类型
		return JSON.toJSONString(obj,PRETTY_FEATURES);
	}
	
	/**
	 * 功能描述：转换对象为JSON串并美化，忽略NULL属性
	 *
	 * @param obj
	 * @return
	 * 
	 * @author yxl
	 *
	 * @since 2017年3月10日
	 */
	public static String toFormatStr(Object obj){
		// 空
		if (obj == null) {
			return StringConstant.EMPTY;
		}
		// 字符串：FastJSON会对特殊字符串进行转义，所以此处直接返回
		if (obj instanceof String) {
			return (String)obj;
		}
		// 其它类型
		return JSON.toJSONString(obj,PRETTY_FEATURES_SHORT);
	}

	/**
	 * 是否为空
	 *
	 * @param  str
	 * @return true = null,'','{}'
	 *
	 * @author 延晓磊
	 * @since 2022年11月04日
	 */
	public static boolean isEmpty(String str){
		return StringUtils.isBlank(str) || str.length() <= 2;
	}
	
	/**
	 * JSON串转换为JSONObject(Map<String, Object>)
	 * 
	 * @param json
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年11月24日
	 */
	public static JSONObject parseObject(String json){
		if (StringUtils.isBlank(json)) {
			return new JSONObject();
		}
		return JSON.parseObject(json);
	}

	/**
	 * 获取 List<JSONObject>
	 *
	 * @param obj
	 * @param key
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年11月24日
	 */
	public static List<JSONObject> getObjs(JSONObject obj, String key){
		return getObjs(obj.getJSONArray(key));
	}

	/**
	 * 将 JSONArray 转换为 List<JSONObject>
	 *
	 * @param arr
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年11月24日
	 */
	public static List<JSONObject> getObjs(JSONArray arr){
		if (CollectionUtils.isEmpty(arr)) {
			return new ArrayList<>();
		}
		List<JSONObject> objs = new ArrayList<>(arr.size());
		for (int i = 0; i < arr.size(); i++) {
			objs.add(arr.getJSONObject(i));
		}
		return objs;
	}

	/**
	 * JSON串转换为Map<String, String>
	 * 
	 * @param json
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年11月24日
	 */
	public static MapSs parseMapSs(String json){
		if (StringUtils.isBlank(json)) {
			return null;
		}
		JSONObject obj = JSON.parseObject(json);
		MapSs map = MapSs.getInstance();
		for (String key : obj.keySet()) {
			map.put(key, obj.getString(key));
		}
		return map;
	}
	
	/**
	 * JSON串转换为JSONArray（List<Object>）
	 * 
	 * @param json
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年11月24日
	 */
	public static JSONArray parseArray(String json){
		if (StringUtils.isBlank(json)) {
			return null;
		}
		return JSON.parseArray(json);
	}
	
	/**
	 * 
	 * 功能描述：转换JSON串为对象
	 *
	 * @param json
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年4月27日
	 */
	public static <T> T parse(String json, Class<T> clazz){
		if (StringUtils.isBlank(json)) {
			return null;
		}
		return JSON.parseObject(json, clazz);
	}
	
	/**
	 * 功能描述：转换JSON串为对象集合
	 *
	 * @param json
	 * @param clazz	集合元素的类型
	 * @return [T]
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年4月27日
	 */
	public static <T> List<T> parseList(String json, Class<T> clazz){
		if (StringUtils.isBlank(json)) {
			return new ArrayList<>();
		}
		return JSON.parseArray(json, clazz);
	}

	/**
	 * 功能描述：转换JSON串为对象集合
	 *
	 * @param json
	 * @param clazz	集合元素的类型
	 * @return [[T]]
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年4月27日
	 */
	public static <T> List<List<T>> parseLists(String json, Class<T> clazz){
		List<List<T>> list = new ArrayList<>();
		if (StringUtils.isBlank(json)) {
			return list;
		}
		JSONArray array = JSON.parseArray(json);
		for (int i = 0; i < array.size(); i++) {
			list.add(array.getJSONArray(i).toJavaList(clazz));
		}
		return list;
	}
	
	/**
	 * 功能描述：转换JSON串为字符串集合
	 *
	 * @param json	"[1,2,3]"
	 * 
	 * @return	['1','2','3']
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年4月27日
	 */
	public static List<String> parseList(String json){
		return parseList(json, String.class);
	}
	
	/**
	 * 
	 * 功能描述：转换JSONObject对象为Java对象
	 * 
	 * 注：如果FastJSON在转换对象的JSON串时，不知道对象的属性的类型，就会转换为JSON对象。如果是对象类型，会转换为JSONObject(Map<String, Object>)
	 *
	 * @param obj
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月6日
	 */
	public static <T> T parse(JSONObject obj, Class<T> clazz){
		if(obj == null){
			return null;
		}
		return JSON.toJavaObject(obj, clazz);
	}

	/**
	 * 转换JSONArray
	 *
	 * @param array
	 * @return JSONObject[]
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年7月6日
	 */
	public static JSONObject[] parseArray(JSONArray array){
		if (array == null) {
			return null;
		}
		JSONObject[] objs = new JSONObject[array.size()];
		for (int i = 0; i < array.size(); i++) {
			objs[i] = array.getJSONObject(i);
		}
		return objs;
	}

	/**
	 * 
	 * 功能描述：转换JSONArray对象为数组
	 * 
	 * 注：如果FastJSON在转换对象的JSON串时，不知道对象的属性的类型，就会转换为JSON对象。如果是数组类型，会转换为JSONArray(List<Object>)
	 *
	 * @param array
	 * @param t
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月6日
	 */
	public static <T> T[] parseArray(JSONArray array, T[] t){
		if (array == null) {
			return null;
		}
		return array.toArray(t);
	}
	
	/**
	 * 
	 * 功能描述：转换json串为数组
	 * 
	 * @param json
	 * @param arr
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月6日
	 */
	public static <T> T[] parseArray(String json, T[] arr){
		if (StringUtils.isBlank(json)) {
			return null;
		}
		return JSON.parseArray(json).toArray(arr);
	}

	/**
	 * 转换JSONArray
	 *
	 * @param array
	 * @return JSONObject[]
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年7月6日
	 */
	public static List<JSONObject> parseList(JSONArray array){
		if (array == null) {
			return null;
		}
		List<JSONObject> objs = new ArrayList<>(array.size());
		for (int i = 0; i < array.size(); i++) {
			objs.add(array.getJSONObject(i));
		}
		return objs;
	}

	/**
	 * 
	 * 功能描述：转换JSONArray对象为Java列表
	 * 
	 * 注：如果FastJSON在转换对象的JSON串时，不知道对象的属性的类型，就会转换为JSON对象。如果是数组类型，会转换为JSONArray(List<Object>)
	 *
	 * @param array
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月6日
	 */
	public static <T> List<T> parseList(JSONArray array, Class<T> clazz){
		if(array == null){
			return null;
		}
		return array.toJavaList(clazz);
	}
	
	/**
	 * 获取JSONObject的指定key的List值
	 * 
	 * @param obj
	 * @param key
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年9月28日
	 */
	public static <T> List<T> getList(JSONObject obj, String key, Class<T> clazz){
		if(obj == null){
			return null;
		}
		return parseList(obj.getJSONArray(key), clazz);
	}
	
	/**
	 * 获取JSONObject的指定key的Obj值
	 * 
	 * @param obj
	 * @param key
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年9月28日
	 */
	public static <T> T getObj(JSONObject obj, String key, Class<T> clazz){
		if(obj == null){
			return null;
		}
		return obj.getObject(key, clazz);
	}

	/**
	 * 转换对象为JSONObject
	 * 
	 * @param o
	 * @return
	 * 
	 * @author 延晓磊
	 * @since 2022年11月04日
	 */
	public static JSONObject toObj(Object o){
		return parseObject(toStr(o));
	}

	/**
	 * 构建JSONObject
	 *
	 * @param kvs	k,v,k,v...
	 * @return
	 * 
	 * @author 延晓磊
	 * @since 2023年11月03日
	 */
	public static JSONObject buildObj(Object ...kvs){
		JSONObject map = new JSONObject();
		if (kvs != null && kvs.length > 0) {
			if (kvs.length % 2 != 0) {
				throw new ParamRuntimeException("参数必须是多组键值对");
			}
			for (int i = 0; i < kvs.length; i += 2) {
				map.put(StringUtil.toString(kvs[i]), kvs[i+1]);
			}
		}
		return map;
	}
	
	/**
	 * 获取扩展属性
	 *
	 * @param json	数据
	 * @param clazz	主表（排除类中已有的字段）
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年11月03日
	 */
	public static JSONObject getExt(final JSONObject json, Class<?> clazz) {
		JSONObject map = new JSONObject();
		if (json != null) {
			// 类字段
			Set<String> fields = ReflectFieldUtil.getFieldNames(clazz);
			for (Map.Entry<String, Object> entry : json.entrySet()) {
				if (!fields.contains(entry.getKey())) {
					// 其它字段
					map.put(entry.getKey(), entry.getValue());
				}
			}
		}
		return map;
	}

	/**
	 * 获取扩展属性
	 *
	 * @param json	数据
	 * @param clazz	主表（排除类中已有的字段）
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年11月03日
	 */
	public static String getExt(final String json, Class<?> clazz) {
		return toStr(getExt(JsonUtil.parseObject(json), clazz));
	}

	/**
	 * FastJSON的JDK8日期处理器
	 *
	 * 问题：FJ默认以yyyy-MM-ddTHH:mm:ss的格式输出日期（可以设置），并以HH:mm:ss.SSS的格式输出时间（无法设置）
	 *
	 * 解决：日期输出yyyy-MM-dd HH:mm:ss的格式
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年6月27日
	 */
	public static class DateSerializer implements ObjectSerializer {

		/**
		 * 日期处理器
		 */
		public static final DateSerializer instance = new DateSerializer();

		/**
		 * 输出对象
		 *
		 * @author 延晓磊
		 *
		 * @since 2019年6月27日
		 *
		 */
		@Override
		public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) {
			// 序列化输出器
			SerializeWriter out = serializer.out;
			if (object == null) {
				// 空
				out.writeNull();
			} else {
				// 格式化日期
				if (object instanceof LocalDateTime) {
					out.writeString(LocalDateUtil.toStr((LocalDateTime)object));
				}else if (object instanceof LocalDate) {
					out.writeString(LocalDateUtil.toStr((LocalDate)object));
				}else if (object instanceof LocalTime) {
					out.writeString(LocalDateUtil.toStr((LocalTime)object));
				}else if (object instanceof Date) {
					out.writeString(DateFormatUtils.format((Date)object, DateConstant.DATE_TIME_PATTERN));
				}else {
					out.writeString(object.toString());
				}
			}
		}
	}

	/**
	 * FastJSON的Long处理器
	 *
	 * 问题：前端JS最多能处理15位数值（最大为9开头的16位数），否则会出现丢精度的问题
	 *
	 * 解决：大数通常是ID类型，须使用字符串输出
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年6月27日
	 */
	public static class LongSerializer implements ObjectSerializer {

		/**
		 * LONG处理器
		 */
		public static final LongSerializer instance = new LongSerializer();

		/**
		 * 输出对象
		 *
		 * @author 延晓磊
		 *
		 * @since 2019年6月27日
		 *
		 */
		@Override
		public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) {
			// 序列化输出器
			SerializeWriter out = serializer.out;
			if (object == null) {
				// 空
				out.writeNull(SerializerFeature.WriteNullNumberAsZero);
			} else {
				// 小数直接输出，大数转字符串
				if (object.toString().length() > 16) {
					out.writeString(object.toString());
				}else{
					out.writeLong((Long) object);
				}
			}
		}
	}

	/**
	 * FastJSON的Long处理器 - 0转null，大数转字符串
	 *
	 * 场景：对于选项框的键为long型，默认值为0时，可以注解字段 @JSONField(serializeUsing = JsonUtil.LongToNullSerializer.class)
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年6月27日
	 */
	public static class LongToNullSerializer implements ObjectSerializer {

		/**
		 * 数值处理器 - 0转null
		 */
		public static final LongToNullSerializer instance = new LongToNullSerializer();

		/**
		 * 输出对象
		 *
		 * @author 延晓磊
		 *
		 * @since 2019年6月27日
		 *
		 */
		@Override
		public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) {
			// 序列化输出器
			SerializeWriter out = serializer.out;
			if (object == null) {
				// 空
				out.writeNull();
			} else {
				// 将0转换为null
				long l = Long.parseLong(object.toString());
				if (l == 0) {
					out.writeNull();
				}else{
					// 小数直接输出，大数转字符串
					if (object.toString().length() > 16) {
						out.writeString(object.toString());
					}else{
						out.writeLong(l);
					}
				}
			}
		}
	}

	/**
	 * FastJSON的Long处理器 - 0转''，大数转字符串
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年6月27日
	 */
	public static class LongToEmptySerializer implements ObjectSerializer {

		/**
		 * 数值处理器 - 0转null
		 */
		public static final LongToEmptySerializer instance = new LongToEmptySerializer();

		/**
		 * 输出对象
		 *
		 * @author 延晓磊
		 *
		 * @since 2019年6月27日
		 *
		 */
		@Override
		public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) {
			// 序列化输出器
			SerializeWriter out = serializer.out;
			if (object == null) {
				// 空
				out.writeString(StringConstant.EMPTY);
			} else {
				// 将0转换为null
				long l = Long.parseLong(object.toString());
				if (l == 0) {
					out.writeString(StringConstant.EMPTY);
				}else{
					// 小数直接输出，大数转字符串
					if (object.toString().length() > 16) {
						out.writeString(object.toString());
					}else{
						out.writeLong(l);
					}
				}
			}
		}
	}

}
